
package pATT.bNEdit.learning;
import java.util.Vector;

import pATT.bNEdit.base.RedBayes;


@SuppressWarnings("unchecked")
public class AlgoritmoK2 extends AlgoritmoAprendizajeEstructural{

    public AlgoritmoK2(int Cota){
        super();
	cotaPadres=Cota;
    }
    /**
     * Permite cambiar la cota superior de padres por variables
     * @param Cota cota de los padres
     */
    public void cambiarCotaPadres(int Cota){
        cotaPadres=Cota;
    }

  //Obtiene un vector con las instanciaciones diferentes de una configuraci?n de padres
  private Vector instanciaciones(Vector padres){
        Vector res= new Vector();
        if(padres.isEmpty()) return res;
        for(int i=0; i<nroCasos; i++){
            Vector nuevo=new Vector();
            for(int j=0; j<padres.size(); j++)
                nuevo.addElement(obtenerEstado(i, ((Integer)padres.get(j)).intValue()));
            if(!pertenece(nuevo, res))
                    res.addElement(nuevo);
        }
        return res;
  }


    //obtiene la cantidad de estados para la variable i
    private int r(int i){
        return ((Vector)estados.get(i)).size();
    }

    //Obtiene el k-?simo estado de la variable i
    private String est(int i, int k){
        return (String)((Vector)estados.get(i)).get(k);
    }

    //n?mero de configuraciones diferentes de padres de la variable i
    private int q(int i){
        return instanciaciones(padres(i)).size();
    }

    //Extrae una configuraci?n de una instanciacion basado en un criterio de padres
    private Vector configuracion(Vector indices, Vector secuencia){
        Vector res=new Vector();
        for(int i=0; i<indices.size(); i++){
            int j= ((Integer)indices.get(i)).intValue();
            res.addElement(secuencia.get(j));
        }
        return res;
    }

    private void Nijk2(int i, int j, int k){
        Vector inst= instanciaciones(padres(i));
        inst=(Vector)inst.get(j);
        //recorre la bd
        for(int n=0; n<nroCasos; n++){
            Vector instancia= (Vector) Datos.get(n);
            if(est(i,k).equals(obtenerEstado(n, i))){
                 Vector conf=configuracion(padres(i), instancia);
                 if(igual(conf, inst)) {
                           NIJK[k]++;
                           NIJ[j]++;
                 }
            }
        }
    }

    //Calcula el factorial de un n?mero
    private int factorial(int n){
        int acum=1;
        while(n>1){
            acum=acum*n;
            n--;
        }
        return acum;
    }

    //Devuelve el valor de la funci?n a maximizar para la variable i
    private double g(int i){
        int nroInstPadres=q(i);
        int Estados=r(i);
        NIJ=new int[nroInstPadres];
        NIJK=new int[Estados];
        for(int a=0; a < nroInstPadres; a++)
           NIJ[a]=0;
        if(nroInstPadres <= 0)
            return 0.0;
        double res=1.0;
        int a1=Estados-1;
        int term1=factorial(a1);
        for(int j=0; j< nroInstPadres; j++){
            double aux2=1.0;
            for(int k=0; k < Estados; k++){
                NIJK[k]=0;
                Nijk2(i,j,k);
                aux2*=factorial(NIJK[k]);
            };
            double aux1=1.0;
            aux1=(double)term1/factorial( NIJ[j]+ a1 );

            res=res*aux1*aux2;
        }
        return res;
    }

    //devuelve el indice del mayor elemento del arreglo
    private int indiceMax(double [] arreglo){
        int m=0;
        for(int i=1; i<arreglo.length; i++){
            if(arreglo[m]<arreglo[i]) m=i;
        }
        return m;
    }
    /**
     * Metodo para aprender la estructura de una red bayesiana en base a un archivo
     * aplicando el algoritmo K2
     * @param nombreArchivo nombre del archivo origen
     */
    public RedBayes aprenderEstructura(String nombreArchivo){
        cargarDesdeArchivo(nombreArchivo);
        double[] valoresG= new double[Variables.size()];
        //Para cada Variable
        for(int i=0; i < Variables.size(); i++){
            int agregados=0; //para cortar cuando supera la cota
            boolean incrementa=true; //si la inclusi?n del padre no incrementa gi corta
            double actual=0;
            while((agregados < cotaPadres) && incrementa){
                //double actual=g(i);
                for(int j=0; j < Variables.size(); j++)
                    if(!existeCamino(i,j)){
                        padres(i).addElement(new Integer(j));
                        valoresG[j]=g(i)-actual;
                        padres(i).remove(padres(i).size()-1);
                    }
                    else valoresG[j]=0;
                if(valoresG[indiceMax(valoresG)]>0){
                    padres(i).addElement(new Integer(indiceMax(valoresG)));
                    agregados++;
                    actual=g(i);
                }
                else
                    incrementa=false;
            }
        }

        //Devuelve la red
        return crearRedBayesiana(nombreArchivo);
    }

    private int[] NIJ;
    private int[] NIJK;
    private int cotaPadres;
}
